class Node {
    children = null;
    name = null;
    value = 0;

    addStack(stackFrames, count) {
        if (stackFrames.length === 0) {
            return;
        }
        if (this.children == null) {
            this.children = [];
        }
        var parentNode = this;
        stackFrames.forEach((stackFrame, index, _) => {
            var node;
            var parentNodeChildren = parentNode.children;
            if (parentNodeChildren != null && parentNodeChildren.length > 0) {
                if (
                    parentNodeChildren[parentNodeChildren.length - 1].name ===
                    stackFrame
                ) {
                    node = parentNodeChildren[parentNodeChildren.length - 1];
                }
            }

            if (node == null) {
                node = new Node();
                node.name = stackFrame;
                node.value = count;
                if (parentNodeChildren == null) {
                    parentNode.children = [];
                }
                parentNode.children.push(node);
            } else {
                node.value += count;
            }
            parentNode = node;
        });
    }
}

// eslint-disable-next-line no-unused-vars
class FlameStack {
    stackFrames = null;
    count = 1;

    // 浅拷贝
    copy(other) {
        this.stackFrames = other.stackFrames;
        this.count = other.count;
        return this;
    }
}

class FlameInfo {
    flameStacks = [];
    stackSumCounts = 0;
}

/**
 * 获取flameStacks的选定区间的数据
 * @param flameStacks
 * @param sampleInterval
 * @param begin
 * @param end
 */

function convertFlameTextToFlameStacks(flameGraphText, reverseFrame) {
    const flameStackList = [];
    flameGraphText.split("\n").forEach((flameStackText) => {
        if (flameStackText.length > 1) {
            const flameStack = new FlameStack();
            const endIndex = flameStackText.lastIndexOf(" ");
            const stack = flameStackText.slice(0, endIndex);
            const count = parseInt(
                flameStackText.slice(endIndex, flameStackText.len)
            );
            let stackFrames = stack.split(";");
            if (reverseFrame) {
                stackFrames.reverse();
            }
            if (stack.charAt(stack.length - 1) === ";") {
                stackFrames.pop();
            }
            flameStack.stackFrames = stackFrames;
            flameStack.count = count;
            flameStackList.push(flameStack);
        }
    });
    return flameStackList;
}

function subFlameStacks(flameStacks, beginIndex, endIndex) {
    let index = 0;
    const flameStackList = [];

    let findBegin = false;
    // 从左到右遍历 找到 符合位置的

    for (let i = 0; i < flameStacks.length; i++) {
        const flameStack = flameStacks[i];
        if (!findBegin) {
            index = index + flameStack.count;

            if (index < beginIndex) {
                // continue step
            } else if (index >= beginIndex) {
                const temp = new FlameStack().copy(flameStack);
                temp.count = index - beginIndex;
                flameStackList.push(temp);
                findBegin = true;
            }
        } else {
            // for find end index
            index = index + flameStack.count;
            if (index < endIndex) {
                flameStackList.push(flameStack);
            } else if (index >= endIndex) {
                const temp = new FlameStack().copy(flameStack);
                temp.count = temp.count - (index - endIndex);
                flameStackList.push(temp);
                break;
            }
        }
    }
    return flameStackList;
}

function convertFlameStackListToNodeTree(flameStacks) {
    var root = new Node();
    flameStacks.forEach((flameStack) => {
        root.addStack(flameStack.stackFrames, flameStack.count);
    });
    if (root.children != null && root.children.length === 1) {
        return root.children[0];
    }
    return root;
}

function convertFlameGraphTextToNodeTree(flameGraphText, reverseFrame) {
    var flameStacks = convertFlameTextToFlameStacks(
        flameGraphText,
        reverseFrame
    );
    return convertFlameStackListToNodeTree(flameStacks);
}

var targetNode = [];
var minNode = [];
var index = 0;
var rootNode;

// show
var flameGraph = d3
    .flamegraph()
    .width(1500)
    .cellHeight(18)
    .transitionDuration(750)
    .minFrameSize(5)
    .transitionEase(d3.easeCubic)
    .sort(false)
    .label(labelHandler)
    //Example to sort in reverse order
    //.sort(function(a,b){ return d3.descending(a.name, b.name);})
    .title("")
    .onClick(onClick)

    .differential(false)
    .inverted(true)
    .setColorMapper(function (d, originalColor) {
        index++;
        if (index == 1) {
            rootNode = d;
        }

        // debugger
        if (d.value <= 1) {
            // debugger
            minNode.push(d);
            return "#FFFFFF";
        }

        // 选中高亮
        if (d.highlight) {
            return "#f18f05";
        }

        var noChildrenNode = d.data.children === null && d.data.value > 1;
        if (noChildrenNode && d.data.value >= 6) {
            d.data.type = "慢函数-1";
            targetNode.push(d);
            return "#e53935";
        }

        // if(d.data.children !== null && d.data.name.indexOf("CRVMain._init") != -1){
        if (d.data.children !== null) {
            var childrenValueSum = 0;
            for (var i = 0; i < d.data.children.length; i++) {
                // debugger
                childrenValueSum = childrenValueSum + d.data.children[i].value;
            }

            if (
                (d.data.value * 1) / 2 >= childrenValueSum &&
                d.data.value >= 6
            ) {
                d.data.type = "慢函数-2";
                targetNode.push(d);
                return "#e53935";
                // return "#e53935";
            }
        }

        //key值
        // 1、自己的大小>大于1/2
        // 2、且兄弟节点我自己最大
        // 3、没有子节点>1/2

        if (!isNaN(d.value) && d.value > (rootNode.value * 1) / 2) {
            var skip = false;

            if (d.parent !== null) {
                for (var i = 0; i < d.parent.children.length; i++) {
                    if (
                        isNaN(d.parent.children[i].value) ||
                        d.parent.children[i].value > d.value
                    ) {
                        console.log("in parent------------");
                        skip = true;
                        break;
                    }
                }
            }

            if (d.data.children !== null) {
                for (var i = 0; i < d.data.children.length; i++) {
                    if (
                        isNaN(d.data.children[i].value) ||
                        d.data.children[i].value > (rootNode.value * 1) / 2
                    ) {
                        // console.log("in child")
                        skip = true;
                        break;
                    }
                }
            }

            if (!skip) {
                d.data.type = "KEY值";
                targetNode.push(d);

                return "#f100d9";
            }
        }

        // multiNode 敏感点高亮
        // 当前函数>1/4且其没有子节点再>1/4
        // 当前函数>1/4且没有子节点
        if (!isNaN(d.value) && d.value > (rootNode.value * 1) / 4) {
            if (d.data.children !== null) {
                var skip = false;
                for (var i = 0; i < d.data.children.length; i++) {
                    if (
                        isNaN(d.data.children[i].value) ||
                        d.data.children[i].value > (rootNode.value * 1) / 4
                    ) {
                        skip = true;
                        break;
                    }
                }

                if (!skip) {
                    d.data.type = "敏感点";
                    targetNode.push(d);

                    return "#f19089";
                }
            } else {
                return "#f19089";
            }
        }


        if(typeof target !== 'undefined'){

        for (var i = target.length - 1; i >= 0; i--) {
            var value = target[i];
            if (d.data.name.indexOf(value) != -1) {
                return "#f1d30e";
            }
        }    
        }
        

        // Thread.sleep这些依然显示
        var otherMethod =
            d.data.name.indexOf("sleep") != -1 ||
            d.data.name.indexOf("await") != -1;

        // android
        var androidOne = d.data.name.indexOf("com.android") != -1;
        var androidTwo = d.data.name.substr(0, 8) == "android.";
        if (!otherMethod && (androidOne || androidTwo)) {
            return "#E1E3E4";
        }
        // java
        var javaOne = d.data.name.indexOf("java.") != -1;
        var javaTwo = d.data.name.indexOf("javax.") != -1;

        if (!otherMethod && (javaOne || javaTwo)) {
            return "#E1E3E4";
        }

        // other skip method
        var otherMethod = d.data.name.indexOf("dexaop") != -1;
        if (otherMethod) {
            return "#E1E3E4";
        }

        // 其他
        return "#56AD5C";
    })
    .selfValue(false);

var details = document.getElementById("details");
flameGraph.setDetailsElement(details);

var nodeTree = convertFlameGraphTextToNodeTree(stackFrameText, false);
d3.select("#chart").datum(nodeTree).call(flameGraph);

document.getElementById("form").addEventListener("submit", function (event) {
    event.preventDefault();
    search();
});

function search() {
    var term = document.getElementById("term").value;
    if (term.length == 0) {
        return;
    }

    flameGraph.search(term);
    console.log(targetNode);
}

function resetZoom() {
    flameGraph.resetZoom();
}

function onClick(d) {
    console.info("Clicked on " + d.data.name);
}

function getQueryVariable(variable) {
    var query = window.location.search.substring(1);
    var vars = query.split("&");
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split("=");
        if (pair[0] == variable) {
            return pair[1];
        }
    }
    return false;
}

// tip显示内容
function labelHandler(d) {
    return (
        (d.data.n || d.data.name) +
        " (" +
        d3.format(".3f")(100 * (d.x1 - d.x0), 3) +
        "%, " +
        d.value +
        " samples" +
        ") " +
        (d.value == 1
            ? "出现1次，耗时小于" + interval + "ms"
            : d.value * (interval ? interval : 50) + " ms")
    );
}

function getQueryVariable() {
    var query = window.location.search.substring(1);
    var vars = query.split("&");
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split("=");
        if (pair[0] == "target") {
            flameGraph.search(pair[1]);
        }
    }
}

function appendBody() {
    var contentStr = "";

    targetNode.sort((one, two) => {
        return two.data.value - one.data.value; // 升序
    });

    // var androidTwo = d.data.name.substr(0, 8) == "android.";

    for (var i = 0; i < targetNode.length; i++) {
        var node = targetNode[i];
        var filter1 = node.data.name.indexOf("dalvik.system.") != -1;
        var filter2 = node.data.name.indexOf("com.android") != -1;
        var filter3 = node.data.name.substr(0, 8) == "android.";
        var filter4 = node.data.name.indexOf("java.") != -1;
        var filter5 = node.data.name.indexOf("javax.") != -1;

        // if(!filter1 && !filter2 && !filter3 && !filter4 && !filter5){
        //     contentStr = contentStr + "耗时：" + (node.data.value * interval)  + "ms ----->" + "类型：" + node.data.type + " ----->"  + node.data.name + "\n";
        //   }

        contentStr =
            contentStr +
            "耗时：" +
            node.data.value * interval +
            "ms ----->" +
            "类型：" +
            node.data.type +
            " ----->" +
            node.data.name +
            "\n";
    }

    //新建一个div元素节点
    var div = document.createElement("div");
    div.innerText = contentStr;
    //把div元素节点添加到body元素节点中成为其子节点，但是放在body的现有子节点的最后
    document.body.appendChild(div);
    //插入到最前面
    document.body.insertBefore(div, document.body.firstElementChild);
}

appendBody();


// ================================================================================================

getQueryVariable();

function clearSearch() {
    document.getElementById("term").value = "";
    flameGraph.clear();
}
